
#pragma once

#ifndef _fmbThread
#define _fmbThread

#include "fmbErr.h"
#include "fmbFn.h"
#include "fmbMutex.h"

#ifdef _WIN32

#include <process.h>
#include <Windows.h>

#else

#include <pthread.h>

#endif

namespace fm {
  namespace base {

class Thread {
public:
  typedef Fn<void, TL_0> RunFn;

private:
#ifdef _WIN32
  typedef uintptr_t Handler;
#else
  typedef pthread_t Handler;
#endif

private:

  RunFn m_fn;
  Handler m_handler;

  Mutex m_startMutex;
  WaitCondition m_startCond;

#ifdef _WIN32
  inline static unsigned int __stdcall threadFn(void *params) {
#else
  inline static void* threadFn(void *params) {
#endif
    Thread* thr = (Thread*)params;
    thr->m_startMutex.lock();
    RunFn fn = thr->m_fn;
    thr->m_startCond.signal();
    thr->m_startMutex.unlock();
    fn();
#ifdef _WIN32
    return 0;
#else
    return NULL;
#endif
  }

public:

  inline Thread(const RunFn &fn) : m_fn(fn), m_handler(0) {
    m_startMutex.lock();
#ifdef _WIN32
    m_handler = _beginthreadex( NULL, 0, Thread::threadFn, this, 0, NULL);
    if (!m_handler) throw Err("Error during Thread::Thread");
#else
    if (pthread_create(&m_handler, NULL, Thread::threadFn, (void*)this))
      throw Err("Error during Thread::Thread");
#endif
    m_startCond.wait(m_startMutex);
    m_startMutex.unlock();
  }

public:

  inline static void join(const Thread &thread) {
#ifdef _WIN32
    _cwait(NULL, thread.m_handler, 0);
#else
    if (pthread_join(thread.m_handler, NULL))
      throw Err("Error during Thread::join");
#endif

  }

  inline static void sleep(const int milis) {
#ifdef _WIN32
    Sleep(milis);
#else
    int sec = milis / 1000;
    timespec time;
    time.tv_sec = sec;
    time.tv_nsec = (milis - sec * 1000) * 1000000L;
    nanosleep(&time, NULL);
#endif

  }

private:
  inline const Handler &handler() const { return m_handler; }

};

  // end of namespace fm::base
  }
// end of namespace fm
}

#endif


// Useful parts, but needed to be inserted into the appropriate file
/*
  inline static int getPID() {
#ifdef _WIN32
    return _getpid();
#else
#endif
  }
*/
